﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[System.Serializable]
public class ScriptBinderItem
{
    public UnityEngine.Object component;
    public string varName;
}

public class ScriptBinder : MonoBehaviour
{
    [HideInInspector] public ScriptBinderItem[] scriptItems = new ScriptBinderItem[0];

    [NonSerialized]
    private int valueIndex;

    protected bool destroyed = false;

    System.Action onEnable, onDisable,onDestroy;
    public void Setup(System.Action onEnable,System.Action onDisable, System.Action onDestroy)
    {
        this.onEnable = onEnable;
        this.onDisable = onDisable;
        this.onDestroy = onDestroy;
    }

    private void OnEnable()
    {
        onEnable?.Invoke();
    }

    private void OnDisable()
    {
        onDisable?.Invoke();
    }

    public void ResetValue()
    {
        valueIndex = 0;
    }
    
    public UnityEngine.Object GetValue()
    {
        return valueIndex >= 0 && valueIndex < scriptItems.Length ? scriptItems[valueIndex++].component : null;
    }

    public UnityEngine.Object GetObject(string key)
    {
        var find = findItemLabel(key);
        if (find >= 0 && find < scriptItems.Length)
        {
            return scriptItems[find].component;
        }
        return null;
    }
    public T GetScript<T>(string key) where T : Component
    {
        var find = findItemLabel(key);
        if (find >= 0 && find < scriptItems.Length)
        {
            return scriptItems[find].component as T;
        }

        Debug.LogErrorFormat("ComScriptBinder GetScript label = {0} error !!!", key);
        return null;
    }

    protected int findItemLabel(string key)
    {
        for (int i = 0; i < scriptItems.Length; ++i)
        {
            if (key == scriptItems[i].varName)
            {
                return i;
            }
        }
        return -1;
    }
    
    #region Invoke   InvokeRepeating
    public System.Action action_base = null;
    public System.Action onceAction_1 = null;
    public System.Action onceAction_2 = null;
    public System.Action onceAction_3 = null;
    public System.Action onceAction_4 = null;
    public System.Action repeatAction_1 = null;
    public System.Action repeatAction_2 = null;
    public System.Action repeatAction_3 = null;
    public void StopInvoke()
    {
        onceAction_1 = null;
        if (IsInvoking("InvokeDelay"))
            CancelInvoke("InvokeDelay");
    }
    public void StopInvoke2()
    {
        onceAction_2 = null;
        if (IsInvoking("InvokeDelay2"))
            CancelInvoke("InvokeDelay2");
    }
    public void StopInvoke3()
    {
        onceAction_3 = null;
        if (IsInvoking("InvokeDelay3"))
            CancelInvoke("InvokeDelay3");
    }
    public void StopInvoke4()
    {
        onceAction_4 = null;
        if (IsInvoking("InvokeDelay4"))
            CancelInvoke("InvokeDelay4");
    }

    public void StopInvokeRepeating()
    {
        repeatAction_1 = null;
        if (IsInvoking("InvokingDelay"))
            CancelInvoke("InvokingDelay");
    }

    public void StopInvokeRepeating2()
    {
        repeatAction_2 = null;
        if (IsInvoking("InvokingDelay2"))
            CancelInvoke("InvokingDelay2");
    }

    public void StopInvokeRepeating3()
    {
        repeatAction_3 = null;
        if (IsInvoking("InvokingDelay3"))
            CancelInvoke("InvokingDelay3");
    }
    
    /// <summary>
    /// 基类专用
    /// </summary>
    public void StopInvokeBase()
    {
        this.action_base = null;
        if (this.IsInvoking("InvokeBaseDelay"))
            this.CancelInvoke("InvokeBaseDelay");
    }

    /// <summary>
    /// 基类专用
    /// </summary>
    /// <param name="time"></param>
    /// <param name="_acticon"></param>
    public void InvokeBase(float time, System.Action _acticon)
    {
        this.action_base = _acticon;
        if (!this.IsInvoking("InvokeBaseDelay"))
            this.Invoke("InvokeBaseDelay", time);
    }

    void InvokeBaseDelay()
    {
        this.action_base?.Invoke();
        this.action_base = null;
    }

    public void Invoke(float time, System.Action _acticon)
    {
        this.onceAction_1 = _acticon;
        if (!this.IsInvoking("InvokeDelay"))
            this.Invoke("InvokeDelay", time);
    }

    public void Invoke2(float time, System.Action _acticon)
    {
        this.onceAction_2 = _acticon;
        if (!this.IsInvoking("InvokeDelay2"))
            this.Invoke("InvokeDelay2", time);
    }

    public void Invoke3(float time, System.Action _acticon)
    {
        this.onceAction_3 = _acticon;
        if (!this.IsInvoking("InvokeDelay3"))
            this.Invoke("InvokeDelay3", time);
    }

    public void Invoke4(float time, System.Action _acticon)
    {
        this.onceAction_4 = _acticon;
        if (!this.IsInvoking("InvokeDelay4"))
            this.Invoke("InvokeDelay4", time);
    }

    public void InvokeRepeating(float time, float repeatRate, System.Action _acticon)
    {
        this.repeatAction_1 = _acticon;
        if (!this.IsInvoking("InvokingDelay"))
            this.InvokeRepeating("InvokingDelay", time, repeatRate);
    }

    public void InvokeRepeating2(float time, float repeatRate, System.Action _acticon)
    {
        this.repeatAction_2 = _acticon;
        if (!this.IsInvoking("InvokingDelay2"))
            this.InvokeRepeating("InvokingDelay2", time, repeatRate);
    }

    public void InvokeRepeating3(float time, float repeatRate, System.Action _acticon)
    {
        this.repeatAction_3 = _acticon;
        if (!this.IsInvoking("InvokingDelay3"))
            this.InvokeRepeating("InvokingDelay3", time, repeatRate);
    }
    
    void InvokeDelay()
    {
        if (onceAction_1 != null)
        {
            onceAction_1();
            onceAction_1 = null;
        }
    }

    void InvokeDelay2()
    {
        if (onceAction_2 != null)
        {
            onceAction_2();
            onceAction_2 = null;
        }
    }

    void InvokeDelay3()
    {
        if (onceAction_3 != null)
        {
            onceAction_3();
            onceAction_3 = null;
        }
    }

    void InvokeDelay4()
    {
        if (onceAction_4 != null)
        {
            onceAction_4();
            onceAction_4 = null;
        }
    }

    void InvokingDelay()
    {
        if (repeatAction_1 != null) repeatAction_1();
    }

    void InvokingDelay2()
    {
        if (repeatAction_2 != null) repeatAction_2();
    }
    
    void InvokingDelay3()
    {
        if (repeatAction_3 != null) repeatAction_3();
    }
    #endregion

    
    public void DestroyWithFrame()
    {
        if (destroyed)
        {
            return;
        }
        destroyed = true;
        StopAllCoroutines();
        StopAllTimers();
        for (int i = 0; i < scriptItems.Length; ++i)
        {
            var scriptItem = scriptItems[i];
            if (null != scriptItem)
            {
                if (scriptItem.component is Button button)
                {
                    button.onClick.RemoveAllListeners();
                    button.onClick.Invoke();
                }
                else if (scriptItem.component is Toggle toggle)
                {
                    toggle.onValueChanged.RemoveAllListeners();
                    toggle.onValueChanged.Invoke(false);
                }
                else if (scriptItem.component is Image img)
                {
                    img.sprite = null;
                }
                if (scriptItem.component is ScriptBinder scriptBinder)
                {
                    scriptBinder.DestroyWithFrame();
                }
            }
            scriptItems[i] = null;
        }
    }
    
    void StopAllTimers()
    {
        action_base = null;
        onceAction_1 = null;
        repeatAction_1 = null;
        repeatAction_2 = null;
        repeatAction_3 = null;
        onceAction_2 = null;
        onceAction_3 = null;
        onceAction_4 = null;
        this.CancelInvoke();
    }
    
    private void OnDestroy()
    {
        Debug.Log($"[=======ScriptBinder=======]:[{gameObject.name}]:[Destroyed]");
        onDestroy?.Invoke();

        this.onEnable = this.onDisable = this.onDestroy = null;

        if (scriptItems!=null&& scriptItems.Length>0)
        {
            for (int i = 0; i < scriptItems.Length; i++)
            {
                scriptItems[i] = null;
            }
        }
    }

}
